/*
 *      Author: Guannan Ma
 *      @mythmgn
 *      Whatever license applied
 */

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

/*
#include "lib.dlt.h"
#include "lib.err.h"
*/

#include "base.h"

dLinkList *initDLinkList(){
	dLinkList *temp=(dLinkList *)malloc(sizeof(dLinkList));
	if(NULL==temp){
		setErrStatusCode(MEMORY_MALLOC_FAILURE);
		return NULL;
	}

	temp->head=NULL;
	temp->tail=NULL;
	temp->length=0;

	return temp;
}
int getDLinkListLength(dLinkList *p_dll){
	if(p_dll!=NULL)
		return p_dll->length;
	else
		return -1;
}
BOOL isDLinkListEmpty(dLinkList *p_dll){
    if(NULL==p_dll->head)
        return TRUE;
    else
        return FALSE;
}
BOOL addDLinkListItem(dLinkList *p_dll,char *key, void *p_data){

    dLinkNode *tempAdd=(dLinkNode *)malloc(sizeof(dLinkNode));

	ASSERT(key!=NULL);
	ASSERT(p_dll!=NULL);
	//ASSERT(p_data);

	if(NULL==tempAdd){
		setErrStatusCode(MEMORY_MALLOC_FAILURE);
		return FALSE;
	}

	/* If no key is provided, store NULL for the pointer key*/
	if(NULL==key){
		tempAdd->key=NULL;
	}else{
		if(NULL==(tempAdd->key=(char *)malloc(sizeof(char)*(strlen(key)+1)))){
			FREEUP(tempAdd);
			setErrStatusCode(MEMORY_MALLOC_FAILURE);
			return FALSE;
		}
		strcpy(tempAdd->key,key);
	}

	tempAdd->data=p_data;

	if(p_dll->head==NULL){ /**< if it's the first add action. */
	    dLinkNode *temp_tail=(dLinkNode *)malloc(sizeof(dLinkNode));
	    temp_tail->key=NULL;
	    temp_tail->data=NULL;
		p_dll->head=tempAdd;
		p_dll->tail=temp_tail;
		temp_tail->next=p_dll->head;
		tempAdd->previous=temp_tail;
	}


	tempAdd->next=p_dll->tail;
	tempAdd->previous=p_dll->tail->previous;

    p_dll->tail->previous=tempAdd;

    if(tempAdd==p_dll->head)
        tempAdd->previous=p_dll->tail;
	tempAdd->previous->next=tempAdd;

	//p_dll->head->previous=p_dll->tail;

	p_dll->length+=1;

	return TRUE;
}


/* Delete a node with its key, if not found, return FALSE;
 * Otherwise return TRUE;*/
BOOL delDLinkListWithKey(dLinkList *p_dll,char *key){

    BOOL rev=FALSE;
    dLinkNode *p_dln,*p_pre;
    ASSERT(p_dll!=NULL);
    if(TRUE==isDLinkListEmpty(p_dll))
        return FALSE;
    if(key==NULL)
    	return FALSE;

    p_dln=p_pre=p_dll->head;
    while(p_dln!=p_dll->tail){

        /**< If the current node's key equals searching string, remove the node from the list */
        if(0==strcmp(p_dln->key,key)){

        	/* If the head node is to be deleted*/
        	if(p_dln==p_dll->head){
        		/*If the headnode is the only item in the list*/
        		if(1==p_dll->length){
        			FREEUP(p_dll->tail);
        			FREEUP(p_dll->head->key);
        			FREEUP(p_dll->head);
        			p_dll->length=0;
        			rev=TRUE;
        			break;
        		}else{ /*If the list has more than 1 nodes/items, free up the node*/

        			/*Make the next node as the head of the dlinklist */
        			p_dll->head=p_dln->next;
        			p_dll->head->next->previous=p_dll->head;

        			/*Set up the previous pointer of the new head node*/
        			p_dll->head->previous=p_dll->tail;

        			FREEUP(p_dln->key);
        			FREEUP(p_dln);
        			p_dll->length-=1;
        			rev=TRUE;
        			break;
        		}
        	}

        	/*If the being deleted node is neither the head nor the tail*/

            p_pre->next=p_dln->next;
            (p_dln->next)->previous=p_pre;
            FREEUP(p_dln->key);
            FREEUP(p_dln);
            p_dll->length-=1;
            rev=TRUE;
            break;
        }
        p_pre=p_dln;
        p_dln=p_dln->next;
    }

    return rev;
}


BOOL delDLinkListWithNodeInfo(dLinkList *p_dll,dLinkNodeInfo *pos){
	/* If also the node is the tail of the list, do nothing
	 */
	if (NULL==p_dll||NULL==pos||pos==p_dll->tail)
		return FALSE;

	/*If it's the head of the list*/
	if(pos==p_dll->head){
		if(1==p_dll->length){ /* If it's both the head node and the only node in the list*/
			FREEUP(pos->key);
			FREEUP(pos);
			FREEUP(p_dll->tail);
			p_dll->length=0;
			return TRUE;
		}else{
			p_dll->head=p_dll->head->next;
			p_dll->head->previous=p_dll->tail;
			p_dll->tail->next=p_dll->head;

			FREEUP(pos->key);
			FREEUP(pos);

			p_dll->length-=1;
			return TRUE;
		}
	}else{ /* If it;s in the middle of the list*/

		pos->previous->next=pos->next;
		pos->next->previous=pos->previous;

		FREEUP(pos->key);
		FREEUP(pos);
		p_dll->length-=1;
		return TRUE;
	}
}

void *getDataDLinkList(dLinkList *p_dll,char *key){

    dLinkNode *p_dln,*p_pre;

    ASSERT(p_dll!=NULL);
    ASSERT(key!=NULL);

    if(TRUE==isDLinkListEmpty(p_dll))
        return NULL;

    p_dln=p_pre=p_dll->head;
    while(p_dln!=p_dll->tail){

        /**< If the current node's key equals searching string, get the node from the list */
        if(0==strcmp(p_dln->key,key)){
            p_pre->next=p_dln->next;
            (p_dln->next)->previous=p_pre;
            return p_dln->data;
        }
        p_pre=p_dln;
        p_dln=p_dln->next;
    }

    return NULL;
}
BOOL emptyDLinkListItem(dLinkList *p_dll){
	if(NULL==p_dll)
		return FALSE;

	dLinkNode *itera,*temp;
	itera=p_dll->head;

	while(itera!=NULL&&itera!=p_dll->tail){
		temp=itera->next;
		FREEUP(itera->key);
		FREEUP(itera);
		itera=temp;
	}


	/*handle the tail pointer and the head*/
	p_dll->head=NULL;
	FREEUP(p_dll->tail);

	p_dll->length=0;
	return TRUE;
}

BOOL freeDLinkList(dLinkList *p_dll,const BOOL freeContent){

	dLinkNode *p_dln,*p_temp;
	int i;

	ASSERT(p_dll!=NULL);

	p_dln=p_dll->head;
	for(i=0;i<p_dll->length;i++){

		p_temp=p_dln->next;

		FREEUP(p_dln->key);
		if(TRUE==freeContent)
			FREEUP(p_dln->data);
		FREEUP(p_dln);

		p_dln=p_temp;

	}
	FREEUP(p_dll->tail);

	FREEUP(p_dll);

	return TRUE;

}


/*
 * The function will return a dLinkNodeInfo until *pos reaches the tail of the linklist.
 * If it reaches the tail, it will return NULL. (Tip: You can use getDLinkListLength to
 * get the length of the list and plan your codes.)
 *
 *  E.g.
 *	dLinkPos *pos=NULL; !!! Important to set pos as NULL before using the function.
 * 	dLinkNodeInfo *p=traverseDLinkListUntilTail(p_dll,&pos);
 *  void *a=p->data;
 *  char *b=p->key;
 *
 * */
dLinkNodeInfo* traverseDLinkListUntilTail(dLinkList *p_dll,dLinkPos **pos){

	dLinkNodeInfo *revTemp;

	/* The first time to visit the function*/
	if(NULL==(*pos)){
		*pos=p_dll->head;
		revTemp=(*pos);
		*pos=(*pos)->next;
		return revTemp;
	}

	/*
	 * If pos is the tail of the Dlinklist, return NULL;
	 */
	if(p_dll->tail==*pos){
		return NULL;
	}else{
		/*If it's not the head or the tail of the Dlinklist */
		revTemp=(*pos);
		*pos=(*pos)->next;
		return revTemp;
	}

}


EXBOOL isTailOfDlinkList(dLinkList *p_dll,dLinkNodeInfo *dinfo){
	if(p_dll==NULL||dinfo==NULL)
		return ERROR;

	if(dinfo==p_dll->tail)
		return TRUE;
	else
		return FALSE;
}

/*
 *  The function will return a dLinkNodeInfo and traverse back to the head like traversing a cycle.
 *  You need to check the tail node (which does not contain any data) by yourself throught function
 *  isTailOfDlinkList.
 *
 *  E.g.
 *	dLinkPos *pos=NULL; !!! Important to set pos as NULL before using the function.
 * 	dLinkNodeInfo *p=traverseDLinkListUntilTail(p_dll,&pos);
 * 	if(FALSE==isTailOfDlinkList(p_dll,p)){
 *  	void *a=p->data;
 *  	char *b=p->key;
 *  }
 *
 * */
dLinkNodeInfo* traverseCycleDLinkList(dLinkList *p_dll,dLinkPos **pos){
	dLinkNodeInfo *revTemp;

	if(p_dll==NULL||pos==NULL)
		return NULL;

	/* The first time to visit the function*/
	if(NULL==(*pos)){
		*pos=p_dll->head;
		revTemp=(*pos);
		*pos=(*pos)->next;
		return revTemp;
	}

	/*It's not the first time to visit the function*/
	revTemp=(*pos)->next;
	return revTemp;

}
